/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import ExamplesJaCoP.Example;
import JaCoP.constraints.SumWeight;
import JaCoP.constraints.XgteqY;
import JaCoP.constraints.XlteqC;
import JaCoP.constraints.XplusYeqC;
import JaCoP.core.FDV;
import JaCoP.core.Store;
import JaCoP.core.Variable;
import JaCoP.search.DepthFirstSearch;
import JaCoP.search.IndomainMin;
import JaCoP.search.Search;
import JaCoP.search.SelectChoicePoint;
import JaCoP.search.SimpleSelect;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Pattern;

/**
 *
 * @author Administrator
 */
public class Knapsack {

    /**
     * It contains all variables used within a specific example.
     */
    public ArrayList<Variable> vars;
    /**
     * It specifies the cost function, null if no cost function is used.
     */
    public Variable cost;
    /**
     * It specifies the constraint store responsible for holding information
     * about constraints and variables.
     */
    public Store store;
    /**
     * It specifies the search procedure used by a given example.
     */
    public Search search;
    FDV profit;
    FDV weight;
    long executeTime;
    int volume;

    /**
     * Return volume of knapsack
     * @return Return volume of knapsack
     */
    public int getVolume() {
        return volume;
    }

    /**
     *Set volume of knapsack
     * @param volume of knapsack
     */
    public void setVolume(int volume) {
        this.volume = volume;
    }

    /**
     * Return algorithm execution time
     * @return Return algorithm execution time
     */
    public long getExecuteTime() {
        return executeTime;
    }

    public boolean searchOptimal() {

        long T1, T2;
        T1 = System.currentTimeMillis();

        SelectChoicePoint select = new SimpleSelect(vars.toArray(new Variable[1]), null,
                new IndomainMin());

        search = new MyDepthFirstSearch();

        boolean result = search.labeling(store, select, cost);


        System.out.println("This is store information:");
        if (result) {
            store.print();
        }

        T2 = System.currentTimeMillis();

        executeTime = T2 - T1;

        return result;

    }

    /**
     *This method create Varaible and imposes constrains
     * @param names names of items
     * @param profits profits of items
     * @param weights weights of items
     * @param noItems number of items
     */
    public void findSolution(String[] names, int[] profits, int[] weights, int noItems) {
        // Creating constraint store
        store = new Store();
        vars = new ArrayList<Variable>();
        // I-th variable represents if i-th item is taken
        FDV quantity[] = new FDV[noItems];
        // Each quantity variable has a domain from 0 to 1
        for (int i = 0; i < quantity.length; i++) {
            quantity[i] = new FDV(store, names[i], 0, 1);

            vars.add(quantity[i]);
        }

        profit = new FDV(store, "Profit", 0, 1000000);
        weight = new FDV(store, "Weight", 0, 1000000);
        store.impose(new SumWeight(quantity, weights, weight));
        store.impose(new SumWeight(quantity, profits, profit));

        //nakładamy ograniczenie i mówie ze łaczna waga nie moze przekraczać
        store.impose(new XlteqC(weight, volume));
        FDV profitNegation = new FDV(store, "ProfitNegation", -100000, 0);

        store.impose(new XplusYeqC(profit, profitNegation, 0));

        cost = profitNegation;

    }

    /**
     * Retuns names of all taken items
     * @return Retuns names of all taken items
     */
    public ArrayList<String> getNamesOfTakenItem() {
        ArrayList<String> lists = new ArrayList<String>();
        Pattern pat = Pattern.compile("[-]");
        for (int i = 0; i < vars.size(); i++) {
            String[] name = pat.split(vars.get(i).id);
            if (!lists.contains(name[0])) {
                lists.add(name[0]);
                //System.out.println(name[0]);
            }

        }

        return lists;
    }

    /**
     * Return names nad quanitities of items
     * @return Return names nad quanitities of items
     */
    public HashMap<String, Integer> getTakenItems() {
        HashMap<String, Integer> results = new HashMap<String, Integer>();
        Pattern pat = Pattern.compile("[-]");
        for (int i = 0; i < vars.size(); i++) {
            if ((vars.get(i).domain).max() == 1) {
                String[] name = pat.split(vars.get(i).id);
                if (results.containsKey(name[0])) {
                    int actualValue = results.get(name[0]);
                    results.remove(name[0]);
                    results.put(name[0], actualValue + 1);
                } else {
                    results.put(name[0], 1);
                }

            }
        }

        return results;
    }

    public StringBuffer printHashMap(HashMap<String, Integer> solut) {
        StringBuffer buff = new StringBuffer();
        Iterator iter = solut.keySet().iterator();
        while (iter.hasNext()) {
            // String key = iter.
            //  buff.append("item " + iter.n)
        }


        return new StringBuffer();
    }

    public String getProfitValue() {
        return profit.domain.max() + "";
    }

    public String getWeightsValue() {
        return weight.domain.max() + "";
    }

    /**
     * Return information about all taken items as string
     * @return Return information about all taken items as string
     */
    public String getTakenItemWithQuantity() {
        StringBuffer buff = new StringBuffer();
        ArrayList<String> nameList = getNamesOfTakenItem();
        HashMap<String, Integer> takenItem = getTakenItems();
        for (int i = 0; i < nameList.size(); i++) {
            if (takenItem.containsKey(nameList.get(i))) {
                buff.append(nameList.get(i) + " qty: " + takenItem.get(nameList.get(i)) + "\n");
            }
        }
        return buff.toString();
    }

    /**
     * Return number of nodes
     * @return number of nodes
     */
    public int getNodes() {
        return search.getNodes();
    }

    /**
     * Return number of decisions
     * @return number of decision
     */
    public int getDecision() {
        return search.getDecisions();
    }

    /**
     * Return number of wrong decision
     * @return
     */
    public int getWrongDecision() {
        return search.getWrongDecisions();
    }

    /**
     * Return number of backtrack
     * @return number of backtrack
     */
    public int getBacktrack() {
        return search.getBacktracks();
    }

    /**
     * 
     * @return
     */
    public String printSolution() {
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < vars.size(); i++) {
            buff.append(vars.get(i).id + " wartość " + (vars.get(i).domain).min() + " " + vars.get(i).weight);
            buff.append("\n");
        }
        buff.append(profit.id + "  " + profit.domain + "\n");

        buff.append(weight.id + "  " + weight.domain);
        return buff.toString();
    }
}
